function attempt(func, params) {
  if (!Array.isArray(func)) {
    func = [func];
  }
  func.forEach((item) => {
    if (typeof item === 'function') {
      item(params);
    }
  });
}

function getExistScript(doc, src) {
  const scripts = doc.getElementsByTagName('script');
  for (let i = 0, len = scripts.length; i < len; i++) {
    if (scripts[i] && scripts[i].src === src) {
      return scripts[i];
    }
  }
}

export default async function loadScript(src, options) {
  return await new Promise((resolve) => {
    options = Object.assign({}, options);
    options.delay = Number.isFinite(options.delay) && options.delay > 0 ? options.delay : 300;
    let doc = options.document || document;
    let script, originOnLoad, originOnError, originOnReadystatechange;
    function done() {
      resolve();
      script.onreadystatechange = null;
      script.onerror = null;
      script.onload = null;
      script = null;
      src = null;
      doc = null;
      options = null;
      originOnLoad = null;
      originOnError = null;
      originOnReadystatechange = null;
    }
    async function checkReady(options) {
      return await new Promise((resolve) => {
        let timer = setInterval(() => {
          if (typeof options.isReady !== 'function' || options.isReady() === true) {
            clearInterval(timer);
            timer = null;
            options = null;
            return resolve();
          }
        }, options.delay);
      });
    }
    async function onLoad() {
      // console.log('onLoad');
      script.setAttribute('loaded', 'true');
      attempt(options.onLoad, script);
      await checkReady(options);
      script.setAttribute('ready', 'true');
      done();
    }
    function onReadystateChange() {
      // console.log('onReadystateChange');
      if (/complete|loaded/.test(script.readyState)) {
        onLoad();
      }
    }
    function onError() {
      // console.log('onError');
      let src = script.src;
      attempt(options.onError, script);
      script.remove();
      done();
      throw new URIError('The script ' + src + ' is not accessible.');
    }

    script = getExistScript(doc, src);
    if (script) {
      if (script.getAttribute('ready') === 'true') {
        return done();
      }
      if (script.getAttribute('loaded') === 'true') {
        return onLoad();
      }
      originOnLoad = script.onload;
      originOnError = script.onerror;
      originOnReadystatechange = script.onreadystatechange;
      script.onload = () => {
        originOnLoad();
        onLoad();
      };
      script.onerror = () => {
        originOnError();
        onError();
      };
      script.onreadystatechange = () => {
        originOnReadystatechange();
        onReadystateChange();
      };
    } else {
      script = doc.createElement('script');
      script.type = 'text/javascript';
      attempt(options.onCreate, script);
      if (src) {
        script.src = src;
        script.onload = onLoad;
        script.onerror = onError;
        script.onreadystatechange = onReadystateChange;
        doc.body.appendChild(script);
      } else {
        doc.body.appendChild(script);
        done();
      }
    }
  });
}
